/**
 * Document Chunker
 * 
 * 文档分块器 - 将长文档分割成适合嵌入和检索的小块
 */

import type { DocumentChunk, KnowledgeBaseConfig } from '@/types/knowledge';

/**
 * 分块配置
 */
export interface ChunkConfig {
  /** 块大小（字符数） */
  chunkSize: number;
  /** 块重叠（字符数） */
  chunkOverlap: number;
  /** 分块策略 */
  strategy?: 'fixed' | 'sentence' | 'paragraph';
}

/**
 * 默认配置
 */
const DEFAULT_CONFIG: ChunkConfig = {
  chunkSize: 1000,
  chunkOverlap: 200,
  strategy: 'sentence',
};

/**
 * 文档分块器类
 */
export class DocumentChunker {
  private config: ChunkConfig;

  constructor(config: Partial<ChunkConfig> = {}) {
    this.config = { ...DEFAULT_CONFIG, ...config };
  }

  /**
   * 分块文档
   */
  chunk(
    content: string,
    documentId: string,
    knowledgeBaseId: string
  ): DocumentChunk[] {
    switch (this.config.strategy) {
      case 'fixed':
        return this.chunkFixed(content, documentId, knowledgeBaseId);
      case 'sentence':
        return this.chunkBySentence(content, documentId, knowledgeBaseId);
      case 'paragraph':
        return this.chunkByParagraph(content, documentId, knowledgeBaseId);
      default:
        return this.chunkBySentence(content, documentId, knowledgeBaseId);
    }
  }

  /**
   * 固定大小分块
   */
  private chunkFixed(
    content: string,
    documentId: string,
    knowledgeBaseId: string
  ): DocumentChunk[] {
    const chunks: DocumentChunk[] = [];
    const { chunkSize, chunkOverlap } = this.config;
    let index = 0;
    let position = 0;

    while (position < content.length) {
      const endPos = Math.min(position + chunkSize, content.length);
      const chunkContent = content.slice(position, endPos);

      chunks.push({
        id: `${documentId}_chunk_${index}`,
        documentId,
        knowledgeBaseId,
        index,
        content: chunkContent.trim(),
        metadata: {
          startChar: position,
          endChar: endPos,
          charCount: chunkContent.length,
        },
        createdAt: new Date(),
      });

      index++;
      position += chunkSize - chunkOverlap;
    }

    return chunks;
  }

  /**
   * 按句子分块
   */
  private chunkBySentence(
    content: string,
    documentId: string,
    knowledgeBaseId: string
  ): DocumentChunk[] {
    const chunks: DocumentChunk[] = [];
    const { chunkSize, chunkOverlap } = this.config;
    
    // 分割成句子
    const sentences = this.splitIntoSentences(content);
    
    let index = 0;
    let currentChunk: string[] = [];
    let currentLength = 0;
    let startChar = 0;

    for (let i = 0; i < sentences.length; i++) {
      const sentence = sentences[i];
      const sentenceLength = sentence.length;

      // 如果当前句子加入后会超过块大小
      if (currentLength + sentenceLength > chunkSize && currentChunk.length > 0) {
        // 保存当前块
        const chunkContent = currentChunk.join(' ');
        chunks.push({
          id: `${documentId}_chunk_${index}`,
          documentId,
          knowledgeBaseId,
          index,
          content: chunkContent.trim(),
          metadata: {
            startChar,
            endChar: startChar + chunkContent.length,
            charCount: chunkContent.length,
          },
          createdAt: new Date(),
        });

        index++;

        // 计算重叠部分
        let overlapChunk: string[] = [];
        let overlapLength = 0;
        for (let j = currentChunk.length - 1; j >= 0; j--) {
          const s = currentChunk[j];
          if (overlapLength + s.length <= chunkOverlap) {
            overlapChunk.unshift(s);
            overlapLength += s.length;
          } else {
            break;
          }
        }

        // 新块从重叠部分开始
        currentChunk = overlapChunk;
        currentLength = overlapLength;
        startChar = startChar + chunkContent.length - overlapLength;
      }

      currentChunk.push(sentence);
      currentLength += sentenceLength;
    }

    // 保存最后一个块
    if (currentChunk.length > 0) {
      const chunkContent = currentChunk.join(' ');
      chunks.push({
        id: `${documentId}_chunk_${index}`,
        documentId,
        knowledgeBaseId,
        index,
        content: chunkContent.trim(),
        metadata: {
          startChar,
          endChar: startChar + chunkContent.length,
          charCount: chunkContent.length,
        },
        createdAt: new Date(),
      });
    }

    return chunks;
  }

  /**
   * 按段落分块
   */
  private chunkByParagraph(
    content: string,
    documentId: string,
    knowledgeBaseId: string
  ): DocumentChunk[] {
    const chunks: DocumentChunk[] = [];
    const { chunkSize, chunkOverlap } = this.config;
    
    // 分割成段落
    const paragraphs = content.split(/\n\n+/).filter(p => p.trim().length > 0);
    
    let index = 0;
    let currentChunk: string[] = [];
    let currentLength = 0;
    let startChar = 0;

    for (const paragraph of paragraphs) {
      const paragraphLength = paragraph.length;

      // 如果单个段落就超过块大小，使用句子分块
      if (paragraphLength > chunkSize * 1.5) {
        // 先保存当前块
        if (currentChunk.length > 0) {
          const chunkContent = currentChunk.join('\n\n');
          chunks.push({
            id: `${documentId}_chunk_${index}`,
            documentId,
            knowledgeBaseId,
            index,
            content: chunkContent.trim(),
            metadata: {
              startChar,
              endChar: startChar + chunkContent.length,
              charCount: chunkContent.length,
            },
            createdAt: new Date(),
          });
          index++;
          currentChunk = [];
          currentLength = 0;
          startChar = startChar + chunkContent.length;
        }

        // 对长段落进行句子分块
        const sentenceChunks = this.chunkBySentence(paragraph, documentId, knowledgeBaseId);
        chunks.push(...sentenceChunks.map(c => ({ ...c, index: index++ })));
        startChar += paragraph.length;
        continue;
      }

      // 如果加入当前段落会超过块大小
      if (currentLength + paragraphLength > chunkSize && currentChunk.length > 0) {
        const chunkContent = currentChunk.join('\n\n');
        chunks.push({
          id: `${documentId}_chunk_${index}`,
          documentId,
          knowledgeBaseId,
          index,
          content: chunkContent.trim(),
          metadata: {
            startChar,
            endChar: startChar + chunkContent.length,
            charCount: chunkContent.length,
          },
          createdAt: new Date(),
        });

        index++;

        // 计算重叠（保留最后一个段落）
        if (currentChunk.length > 0 && currentChunk[currentChunk.length - 1].length <= chunkOverlap) {
          const lastParagraph = currentChunk[currentChunk.length - 1];
          currentChunk = [lastParagraph];
          currentLength = lastParagraph.length;
          startChar = startChar + chunkContent.length - lastParagraph.length;
        } else {
          currentChunk = [];
          currentLength = 0;
          startChar = startChar + chunkContent.length;
        }
      }

      currentChunk.push(paragraph);
      currentLength += paragraphLength;
    }

    // 保存最后一个块
    if (currentChunk.length > 0) {
      const chunkContent = currentChunk.join('\n\n');
      chunks.push({
        id: `${documentId}_chunk_${index}`,
        documentId,
        knowledgeBaseId,
        index,
        content: chunkContent.trim(),
        metadata: {
          startChar,
          endChar: startChar + chunkContent.length,
          charCount: chunkContent.length,
        },
        createdAt: new Date(),
      });
    }

    return chunks;
  }

  /**
   * 将文本分割成句子
   */
  private splitIntoSentences(text: string): string[] {
    // 简单的句子分割（支持中英文）
    // 中文句号、问号、感叹号；英文句号、问号、感叹号
    const sentenceRegex = /[^.!?。！？]+[.!?。！？]+/g;
    const matches = text.match(sentenceRegex);
    
    if (!matches) {
      // 如果没有匹配到句子结束符，按行分割
      return text.split('\n').filter(s => s.trim().length > 0);
    }

    return matches.map(s => s.trim()).filter(s => s.length > 0);
  }

  /**
   * 估计分块结果
   */
  estimateChunks(content: string): {
    estimatedChunkCount: number;
    averageChunkSize: number;
  } {
    const { chunkSize, chunkOverlap } = this.config;
    const contentLength = content.length;
    
    // 估算块数量（考虑重叠）
    const effectiveChunkSize = chunkSize - chunkOverlap;
    const estimatedChunkCount = Math.ceil(contentLength / effectiveChunkSize);
    const averageChunkSize = contentLength / estimatedChunkCount;

    return {
      estimatedChunkCount,
      averageChunkSize: Math.round(averageChunkSize),
    };
  }

  /**
   * 验证块
   */
  validateChunks(chunks: DocumentChunk[]): {
    valid: boolean;
    errors: string[];
  } {
    const errors: string[] = [];

    // 检查块是否为空
    if (chunks.length === 0) {
      errors.push('No chunks generated');
      return { valid: false, errors };
    }

    // 检查每个块
    chunks.forEach((chunk, i) => {
      if (!chunk.content || chunk.content.trim().length === 0) {
        errors.push(`Chunk ${i} has empty content`);
      }
      if (chunk.index !== i) {
        errors.push(`Chunk ${i} has incorrect index: ${chunk.index}`);
      }
      if (chunk.metadata.charCount <= 0) {
        errors.push(`Chunk ${i} has invalid char count: ${chunk.metadata.charCount}`);
      }
    });

    return {
      valid: errors.length === 0,
      errors,
    };
  }

  /**
   * 更新配置
   */
  updateConfig(config: Partial<ChunkConfig>): void {
    this.config = { ...this.config, ...config };
  }

  /**
   * 获取当前配置
   */
  getConfig(): ChunkConfig {
    return { ...this.config };
  }
}

/**
 * 创建文档分块器实例
 */
export function createDocumentChunker(config?: Partial<ChunkConfig>): DocumentChunker {
  return new DocumentChunker(config);
}

